\section{Fonctionnement global}

Trois types d'entités sont utilisés pour la gestion des threads: une structure représentant les threads, des listes de threads, ainsi que les méthodes de gestion des threads.

\bigskip

La structure d'un thread contient son contexte, un pointeur vers un thread qui \og{}attend\fg{} ce thread, une valeur de retour ainsi qu'une priorité (utilisée pour la préemption).

\bigskip

En ce qui concerne les listes, nous avons décidé d'utiliser des \textsf{GList}. En effet, la documentation semblait claire, il n'y avait rien à installer pour pouvoir compiler (étant donné que nous utilisons \textsf{GNOME} sur nos distributions) et nous pensions que, étant donné la forte utilisation de \textsf{GNOME}, leur implémentation était optimale. Il y a 3 listes : une liste contenant les threads actifs, une liste contenant les threads \og{}endormis\fg{} et une liste contenant les threads terminés. Citons également le pointeur sur le thread actuellement en exécution.

\bigskip

La partie principale de ce projet est bien évidemment constituée des méthodes gérant les threads. Nous allons les présenter petit à petit en s'intéressant à leurs différentes propriétés.

\subsection{Thread principal}
Grâce à l'utilisation de \verb+__attribute__((constructor))+, nous faisons en sorte que le \verb+main+ soit considéré comme un thread. Ce mécanisme permet d'initialiser le pointeur sur le thread courant avant l'exécution du main.

\subsection{Création d'un thread}
La création d'un thread (\verb+thread_create()+) consiste simplement à créer un nouveau contexte avec la fonction donnée en paramètre, à allouer la mémoire du thread et à l'initialiser, ainsi que de l'ajouter à la liste des threads actifs.

\subsection{Passage de main entre threads}
Cette possibilité est offerte grâce aux fonctions \verb+thread_next()+ et \verb+thread_yield()+. La dernière consiste à ajouter le thread à la fin de la liste des threads actifs et à appeler \verb+thread_next()+. Cette fonction récupère le premier thread de la liste des threads actifs et le place à la place du thread en exécution. Remarquons que cette fonction ne fait aucune opération sur le précédent thread en exécution : c'est aux fonctions appelantes de le sauvegarder / placer dans une liste. Enfin, un changement de contexte est effectué.

\subsection{Attente d'un thread}
La fonction \verb+thread_join()+ permet d'attendre un thread. Deux cas sont possibles. Dans le premier cas, le thread qu'on doit attendre est déjà terminé et il se trouve dans la liste des threads terminés. On récupère alors la valeur de retour si nécessaire et on sort de la fonction. Dans le deuxième cas, le thread n'est pas terminé et on se met dans la liste des processus endormis et on met à jour la structure du processus attendu en lui spécifiant qu'on l'attend.

\subsection{Fin d'un thread}
Lorsqu'un thread appelle la fonction \verb+thread_exit()+, on stocke la valeur passée en paramètre qui correspond à la valeur de retour dans la structure du thread. On ajoute le thread à la liste des processus terminés. Si un processus nous attend, on le réveille. Et on passe la main à un autre processus. Pour que ce mécanisme fonctionne même si l'utilisateur n'appelle pas \verb+thread_exit()+ à la fin des ces fonctions, lors de la création d'un thread, la fonction appelée n'est pas directement la fonction passée en paramètre mais \verb+thread_exec()+ qui se charge d'appeler la fonction en récupérant la valeur de retour et d'appeler \verb+thread_exit()+ avec cette valeur.

\subsection{Connaissance du thread courant}
La fonction \verb+thread_self()+ renvoie simplement le pointeur du thread en exécution. 

\subsection{Préemption des threads}
La préemption des threads a été réalisée à l'aide d'un timer. Chaque fois que l'on rend la main à un thread, on initialise un timer qui, quand il arrive à 0, envoie un signal \textsf{SIGVALARM}. En mettant en place un gestionnaire de signaux pour ce signal en particulier, on peut récupérer la main et on peut ainsi appeler la fonction \verb+thread_yield()+ et donc passer la main à un autre thread. À chaque fois qu'une fonction touchant à la gestion des threads est appelée, il faut désactiver le timer pour éviter de se faire préempter à un moment où nos structures de données sont dans des états incohérents.

\subsection{Priorité}
Dans chaque structure décrivant un thread, il y a un entier définissant la priorité du thread. Pour définir cet entier, une méthode \verb+thread_priority()+ a été ajoutée. Elle prend en paramètre un \verb+thread_t+ et un entier et met à jour la priorité du thread en fonction de l'entier. Les priorités varient entre -20 et 20 avec comme priorité par défaut 0. Pour l'instant la priorité influe sur la durée du timer de la préemption. Et donc plus la priorité est petite, plus la \textit{time slice} du processus en cours sera long et vice versa.

\subsection{Gestion de la mémoire}
Deux allocations de mémoire sont à gérer. Une est effectuée par l'ajout dans une \textsf{GList}, l'autre est effectuée pour allouer la mémoire d'un thread. Nous utilisons la fonction \verb+g_list_remove()+ pour libérer la mémoire de la liste et la fonction créée \verb+thread_free()+ pour libérer la mémoire du thread. La mémoire est libérée dans deux situations : dans la fonction \verb+thread_join()+ et à la fin du programme. Dans le premier cas, si le thread que l'on doit attendre est déjà terminé (il se trouve dans la liste des threads terminés), on peut alors l'enlever de cette liste et libérer la mémoire du thread. En effet, le manuel de \verb+pthread_join()+ nous indique que au plus un seul thread peut attendre la mort d'un thread donné. Notre fonction renvoie une erreur si l'utilisateur effectue deux \verb+thread_join()+ sur le même thread. Mais elle ne gère pas encore le cas où le thread que l'on doit attendre a déjà été attendu. Dans le second cas (terminaison du programme) grâce à \verb+__attribute__((destructor))+, nous pouvons libérer correctement la mémoire. Nous utilisons une fonction dédiée \verb+queue_free()+ pour libérer la mémoire des threads dans les listes et des éléments de la liste et nous libérons également le thread en exécution.
